/*
 * Copyright 2009 SHOP.COM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shop.opensocial.mediated.shindig.spi;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.shop.opensocial.mediated.config.MediatedDriverFactory;
import com.shop.opensocial.mediated.services.ActivityServiceMediator;
import com.shop.opensocial.mediated.services.AppDataServiceMediator;
import com.shop.opensocial.mediated.services.AuthenticationHandlerMediator;
import com.shop.opensocial.mediated.services.OAuthStoreMediator;
import com.shop.opensocial.mediated.services.PersonServiceMediator;
import com.shop.opensocial.mediated.shindig.factories.*;
import com.shop.opensocial.mediated.shindig.request.MediatedServletFilter;
import com.shop.opensocial.mediated.types.ActivityMediator;
import com.shop.opensocial.mediated.types.DataCollectionMediator;
import com.shop.opensocial.mediated.services.OAuthDataStoreMediator;
import com.shop.opensocial.mediated.types.PersonMediator;
import com.shop.opensocial.mediated.types.SecurityTokenMediator;
import com.shop.opensocial.mediated.types.OAuthEntryMediator;
import com.shop.opensocial.mediated.types.OAuthConsumerMediator;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthServiceProvider;
import org.apache.shindig.auth.AuthenticationHandler;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.auth.UrlParameterAuthenticationHandler;
import org.apache.shindig.common.util.ImmediateFuture;
import org.apache.shindig.gadgets.GadgetException;
import org.apache.shindig.gadgets.oauth.OAuthStore;
import org.apache.shindig.protocol.DataCollection;
import org.apache.shindig.protocol.ResponseError;
import org.apache.shindig.protocol.RestfulCollection;
import org.apache.shindig.social.opensocial.model.Activity;
import org.apache.shindig.social.opensocial.model.Person;
import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
import org.apache.shindig.social.opensocial.oauth.OAuthEntry;
import org.apache.shindig.social.opensocial.spi.ActivityService;
import org.apache.shindig.social.opensocial.spi.AppDataService;
import org.apache.shindig.social.opensocial.spi.CollectionOptions;
import org.apache.shindig.social.opensocial.spi.GroupId;
import org.apache.shindig.social.opensocial.spi.PersonService;
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;

/**
 * Does the work of interfacing with the Mediated instance and Shindig
 *
 * @author Jordan Zimmerman
 */
@Singleton
@SuppressWarnings({"deprecation"})
public class MediatedOpenSocialService implements ActivityService, PersonService, AppDataService, AuthenticationHandler, OAuthStore, OAuthDataStore
{
	@Inject
	public MediatedOpenSocialService(UrlParameterAuthenticationHandler urlHandler) throws Exception
	{
		fUrlHandler = urlHandler;
	}

	@Override
	public OAuthEntry getEntry(String oauthToken)
	{
		OAuthEntryMediator entry = oathDataStore().getEntry(MediatedServletFilter.getRequest(), oauthToken);
		return OAuthEntryMediatorFactory.i.make(entry);
	}

	@Override
	public SecurityToken getSecurityTokenForConsumerRequest(String consumerKey, String userId)
	{
		SecurityTokenMediator token = oathDataStore().getSecurityTokenForConsumerRequest(MediatedServletFilter.getRequest(), consumerKey, userId);
		return SecurityTokenMediatorFactory.i.make(token);
	}

	@Override
	public OAuthConsumer getConsumer(String consumerKey)
	{
		OAuthConsumerMediator token = oathDataStore().getConsumer(MediatedServletFilter.getRequest(), consumerKey);
		return OAuthConsumerMediatorFactory.i.make(token);
	}

	@Override
	public OAuthEntry generateRequestToken(String consumerKey)
	{
		OAuthEntryMediator entry = oathDataStore().generateRequestToken(MediatedServletFilter.getRequest(), consumerKey);
		return OAuthEntryMediatorFactory.i.make(entry);
	}

	@Override
	public OAuthEntry convertToAccessToken(OAuthEntry entry)
	{
		OAuthEntryMediator convertedEntry = oathDataStore().convertToAccessToken(MediatedServletFilter.getRequest(), OAuthEntryMediatorFactory.i.unmake(entry));
		return OAuthEntryMediatorFactory.i.make(convertedEntry);
	}

	@Override
	public void authorizeToken(OAuthEntry entry, String userId)
	{
		oathDataStore().authorizeToken(MediatedServletFilter.getRequest(), OAuthEntryMediatorFactory.i.unmake(entry), userId);
	}

	@Override
	public ConsumerInfo getConsumerKeyAndSecret(SecurityToken securityToken, String serviceName, OAuthServiceProvider provider) throws GadgetException
	{
		try
		{
			OAuthStoreMediator.ConsumerInfo 	info = oathStore().getConsumerKeyAndSecret
			(
				MediatedServletFilter.getRequest(),
				SecurityTokenMediatorFactory.i.unmake(securityToken),
				serviceName,
				OAuthServiceProviderMediatorFactory.i.unmake(provider)
			);
			return OAuthStoreMediatorConsumerInfoFactory.i.make(info);
		}
		catch ( Exception e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public TokenInfo getTokenInfo(SecurityToken securityToken, ConsumerInfo consumerInfo, String serviceName, String tokenName) throws GadgetException
	{
		try
		{
			OAuthStoreMediator.TokenInfo info = oathStore().getTokenInfo
			(
				MediatedServletFilter.getRequest(),
				SecurityTokenMediatorFactory.i.unmake(securityToken),
				OAuthStoreMediatorConsumerInfoFactory.i.unmake(consumerInfo),
				serviceName,
				tokenName
			);
			return OAuthStoreMediatorTokenInfoFactory.i.make(info);
		}
		catch ( Exception e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public void setTokenInfo(SecurityToken securityToken, ConsumerInfo consumerInfo, String serviceName, String tokenName, TokenInfo tokenInfo) throws GadgetException
	{
		try
		{
			oathStore().setTokenInfo
			(
				MediatedServletFilter.getRequest(),
				SecurityTokenMediatorFactory.i.unmake(securityToken),
				OAuthStoreMediatorConsumerInfoFactory.i.unmake(consumerInfo),
				serviceName,
				tokenName,
				OAuthStoreMediatorTokenInfoFactory.i.unmake(tokenInfo)
			);
		}
		catch ( Exception e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public void removeToken(SecurityToken securityToken, ConsumerInfo consumerInfo, String serviceName, String tokenName) throws GadgetException
	{
		try
		{
			oathStore().removeToken
			(
				MediatedServletFilter.getRequest(),
				SecurityTokenMediatorFactory.i.unmake(securityToken),
				OAuthStoreMediatorConsumerInfoFactory.i.unmake(consumerInfo),
				serviceName,
				tokenName
			);
		}
		catch ( Exception e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public String getName()
	{
		return "Mediated";
	}

	@Override
	public SecurityToken getSecurityTokenFromRequest(HttpServletRequest request) throws InvalidAuthenticationException
	{
		AuthenticationHandlerMediator 	handler = authenticationHandler();
		if ( handler != null )
		{
			SecurityTokenMediator 		token = handler.getSecurityTokenFromRequest(request);
			return SecurityTokenMediatorFactory.i.make(token);
		}
		return fUrlHandler.getSecurityTokenFromRequest(request);
	}

	@Override
	public String getWWWAuthenticateHeader(String realm)
	{
		AuthenticationHandlerMediator 	handler = authenticationHandler();
		if ( handler != null )
		{
			return handler.getWWWAuthenticateHeader(realm);
		}
		return fUrlHandler.getWWWAuthenticateHeader(realm);
	}

	@Override
	public Future<RestfulCollection<Activity>> getActivities(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token) throws SocialSpiException
	{
		try
		{
			List<ActivityMediator> activities = activityService().getActivities(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmakeSet(userIds), GroupIdMediatorFactory.i.unmake(groupId), appId, fields, CollectionOptionsMediatorFactory.i.unmake(options), SecurityTokenMediatorFactory.i.unmake(token));
			return unmarshall(ActivityMediatorFactory.i, activities);
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public Future<RestfulCollection<Activity>> getActivities(UserId userId, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token) throws SocialSpiException
	{
		try
		{
			List<ActivityMediator> activities = activityService().getActivities(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), GroupIdMediatorFactory.i.unmake(groupId), appId, fields, CollectionOptionsMediatorFactory.i.unmake(options), activityIds, SecurityTokenMediatorFactory.i.unmake(token));
			return unmarshall(ActivityMediatorFactory.i, activities);
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public Future<Activity> getActivity(UserId userId, GroupId groupId, String appId, Set<String> fields, String activityId, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			ActivityMediator activity = activityService().getActivity(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), GroupIdMediatorFactory.i.unmake(groupId), appId, fields, activityId, SecurityTokenMediatorFactory.i.unmake(securityToken));
			return ImmediateFuture.newInstance(ActivityMediatorFactory.i.make(activity));
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public Future<Void> deleteActivities(UserId userId, GroupId groupId, String appId, Set<String> activityIds, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			activityService().deleteActivities(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), GroupIdMediatorFactory.i.unmake(groupId), appId, activityIds, SecurityTokenMediatorFactory.i.unmake(securityToken));
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
		return ImmediateFuture.newInstance(null);
	}

	@Override
	public Future<Void> createActivity(UserId userId, GroupId groupId, String appId, Set<String> fields, Activity activity, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			activityService().createActivity(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), GroupIdMediatorFactory.i.unmake(groupId), appId, fields, ActivityMediatorFactory.i.unmake(activity), SecurityTokenMediatorFactory.i.unmake(securityToken));
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
		return ImmediateFuture.newInstance(null);
	}

	@Override
	public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds, GroupId groupId, CollectionOptions collectionOptions, Set<String> fields, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			List<PersonMediator> 	people = personService().getPeople(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmakeSet(userIds), GroupIdMediatorFactory.i.unmake(groupId), null, fields, SecurityTokenMediatorFactory.i.unmake(securityToken));
			return unmarshall(PersonMediatorFactory.i, people);
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public Future<Person> getPerson(UserId userId, Set<String> strings, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			PersonMediator person = personService().getPerson(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), strings, SecurityTokenMediatorFactory.i.unmake(securityToken));
			return ImmediateFuture.newInstance(PersonMediatorFactory.i.make(person));
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
	}

	@Override
	public Future<DataCollection> getPersonData(Set<UserId> userIds, GroupId groupId, String appId, Set<String> strings, SecurityToken securityToken) throws SocialSpiException
	{
		DataCollection collection;
		try
		{
			DataCollectionMediator data = appDataService().getPersonData(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmakeSet(userIds), GroupIdMediatorFactory.i.unmake(groupId), appId, strings, SecurityTokenMediatorFactory.i.unmake(securityToken));
			collection = DataCollectionMediatorFactory.i.make(data);
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
		return ImmediateFuture.newInstance(collection);
	}

	@Override
	public Future<Void> deletePersonData(UserId userId, GroupId groupId, String appId, Set<String> fields, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			appDataService().deletePersonData(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), GroupIdMediatorFactory.i.unmake(groupId), appId, fields, SecurityTokenMediatorFactory.i.unmake(securityToken));
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
		return ImmediateFuture.newInstance(null);
	}

	@Override
	public Future<Void> updatePersonData(UserId userId, GroupId groupId, String appId, Set<String> fields, Map<String, String> values, SecurityToken securityToken) throws SocialSpiException
	{
		try
		{
			appDataService().updatePersonData(MediatedServletFilter.getRequest(), UserIdMediatorFactory.i.unmake(userId), GroupIdMediatorFactory.i.unmake(groupId), appId, fields, values, SecurityTokenMediatorFactory.i.unmake(securityToken));
		}
		catch ( Throwable e )
		{
			MediatedDriverFactory.getDriver().exceptionThrown(e);
			throw new SocialSpiException(ResponseError.INTERNAL_ERROR, e.getMessage(), e);
		}
		return ImmediateFuture.newInstance(null);
	}

	private OAuthDataStoreMediator oathDataStore()
	{
		return MediatedDriverFactory.getDriver().getOAuthDataStoreMediator();
	}

	private OAuthStoreMediator		oathStore()
	{
		return MediatedDriverFactory.getDriver().getOAuthStoreMediator();
	}

	private AppDataServiceMediator appDataService()
	{
		return MediatedDriverFactory.getDriver().getAppDataServiceMediator();
	}

	private ActivityServiceMediator activityService()
	{
		return MediatedDriverFactory.getDriver().getActivityServiceMediator();
	}

	private PersonServiceMediator personService()
	{
		return MediatedDriverFactory.getDriver().getPersonServiceMediator();
	}

	private AuthenticationHandlerMediator	authenticationHandler()
	{
		return MediatedDriverFactory.getDriver().getAuthenticationHandlerMediator();
	}

	private static<SHINDIG, MEDIATED> Future<RestfulCollection<SHINDIG>> unmarshall(GenericMediatorFactoryBase<SHINDIG, MEDIATED> factory, List<MEDIATED> l)
	{
		List<SHINDIG> 			unmarshalled = new ArrayList<SHINDIG>();
		for ( MEDIATED m : l )
		{
			unmarshalled.add(factory.make(m));
		}

		return ImmediateFuture.newInstance(new RestfulCollection<SHINDIG>(unmarshalled));
	}

	private final UrlParameterAuthenticationHandler fUrlHandler;
}
